package magnet.processor.instances.parser;

import magnet.processor.MagnetProcessorEnv;
import magnet.processor.common.ValidationException;
import magnet.processor.instances.aspects.classifier.ClassifierAttributeParser;
import magnet.processor.instances.aspects.disabled.DisabledAttributeParser;
import magnet.processor.instances.aspects.disposer.DisposerAttributeParser;
import magnet.processor.instances.aspects.factory.FactoryAttributeParser;
import magnet.processor.instances.aspects.limitedto.LimitedToAttributeParser;
import magnet.processor.instances.aspects.scoping.ScopingAttributeParser;
import magnet.processor.instances.aspects.selector.SelectorAttributeParser;
import magnet.processor.instances.aspects.type.TypeAttributeParser;
import magnet.processor.instances.aspects.type.TypesAttributeParser;

import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import java.util.HashMap;
import java.util.Map;

public abstract class AttributeParser {

    private String name;

    public AttributeParser(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public static class Scope<E extends Element> {

        private boolean isTypeInheritanceEnforced;
        private ParserInstance<E> instance;
        private Element element;
        private MagnetProcessorEnv env;

        public Scope(boolean isTypeInheritanceEnforced,ParserInstance<E> instance, Element element, MagnetProcessorEnv env) {
            this.isTypeInheritanceEnforced = isTypeInheritanceEnforced;
            this.instance = instance;
            this.element = element;
            this.env = env;
        }

        public boolean isTypeInheritanceEnforced() {
            return isTypeInheritanceEnforced;
        }

        public void setTypeInheritanceEnforced(boolean typeInheritanceEnforced) {
            isTypeInheritanceEnforced = typeInheritanceEnforced;
        }

        public ParserInstance<E> getInstance() {
            return instance;
        }

        public void setInstance(ParserInstance<E> instance) {
            this.instance = instance;
        }

        public Element getElement() {
            return element;
        }

        public void setElement(Element element) {
            this.element = element;
        }

        public MagnetProcessorEnv getEnv() {
            return env;
        }

        public void setEnv(MagnetProcessorEnv env) {
            this.env = env;
        }

        public Scope copy() {
            return new Scope(isTypeInheritanceEnforced, instance, element, env);
        }
    }

    public abstract <E extends Element> ParserInstance<E> parse(Scope<E> scope, AnnotationValue value) throws ValidationException;

    public static class Registry {

        private static volatile Registry registry;
        public Map<String, AttributeParser> PARSERS;

        private Registry() {
            PARSERS = new HashMap<>();
            PARSERS.put(ClassifierAttributeParser.INSTANCE().getName(), ClassifierAttributeParser.INSTANCE());
            PARSERS.put(DisabledAttributeParser.INSTANCE().getName(),DisabledAttributeParser.INSTANCE());
            PARSERS.put(DisposerAttributeParser.INSTANCE().getName(),DisposerAttributeParser.INSTANCE());
            PARSERS.put(FactoryAttributeParser.INSTANCE().getName(),FactoryAttributeParser.INSTANCE());
            PARSERS.put(LimitedToAttributeParser.INSTANCE().getName(),LimitedToAttributeParser.INSTANCE());
            PARSERS.put(ScopingAttributeParser.INSTANCE().getName(),ScopingAttributeParser.INSTANCE());
            PARSERS.put(SelectorAttributeParser.INSTANCE().getName(),SelectorAttributeParser.INSTANCE());
            PARSERS.put(TypeAttributeParser.INSTANCE().getName(),TypeAttributeParser.INSTANCE());
            PARSERS.put(TypesAttributeParser.INSTANCE().getName(),TypesAttributeParser.INSTANCE());
        }

        public static Registry getInstance() {
            synchronized (Registry.class) {
                if (registry == null) {
                    synchronized (Registry.class) {
                        registry = new Registry();
                    }
                }
            }
            return registry;
        }

    }

}
